﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace GameStateManagement
{
    public class WormholeObject
    {
        public Actor m_Obj;
        public float m_fMultiplier; // Scale the magnitude of wormhole force on this object
        public float m_fMinDist; // The object must be within this distance to be affected by a wormhole.  0 means no min.

        public int lastTeleport;

        public WormholeObject(Actor obj)
        {
            m_Obj = obj;
            m_fMultiplier = 1f;
            m_fMinDist = 0f;

            lastTeleport = 0;
        }

        public WormholeObject(Actor obj, float multiplier, float minDist)
        {
            m_Obj = obj;
            m_fMultiplier = multiplier;
            m_fMinDist = minDist;

            lastTeleport = 0;
        }
    }

    /// <summary>
    /// This is a singleton class.  Call InitializeWormholeManager to get the reference.
    /// </summary>
    class WormholeManager : Microsoft.Xna.Framework.GameComponent
    {
        public static int MinWormholePairLifetime = 10;
        public static int MaxWormholePairLifetime = 60;
        public static float MinWormholeMagnitudeRatio = 0.3f;
        public static float MaxWormholeMagnitudeRatio = 0.9f;
        public static float MinWormholeSpeed = 0.2f;
        public static float MaxWormholeSpeed = 0.3f;
        public static float WormholePairDensity = (1f / 20000000f); // One wormhole pair per WormholePairDensity units cubed.
        private static WormholeManager wormholeManager = null;
        
        Random rand = new Random();

        // A list of actors in scope of the black hole.  (Affected by it's gravity)
        public static List<WormholeObject> wormholeObjs = new List<WormholeObject>();

        public List<Wormhole> wormholes = new List<Wormhole>();

        private Ship m_Ship;
        private int m_iIdealNumWormholePairs;

        private WormholeManager(Game game)
            : base(game)
        {
        }

        public override void Initialize()
        {
            CreateRandomWormholePair();

            base.Initialize();
        }

        public static WormholeManager InitializeWormholeManager(Game game)
        {
            if (wormholeManager == null)
            {
                wormholeManager = new WormholeManager(game);
                game.Components.Add(wormholeManager);
            }
            return wormholeManager;
        }

        public static void DestroyWormholeManager(Game game)
        {
            // Delete all the wormholes.
            foreach (WormholeObject wormholeObj in wormholeObjs)
            {
                game.Components.Remove(wormholeObj.m_Obj);
            }
            wormholeObjs.Clear();

            // Remove from game.
            game.Components.Remove(wormholeManager);

            // Drop the reference.
            wormholeManager = null;
        }

        public static WormholeManager GetWormholeManager()
        {
            return wormholeManager;
        }

        public override void Update(GameTime gameTime)
        {
            // Update Actor.withinWormholeRadius
            if (!GameplayScreen.pause && !GameplayScreen.gameOver)
            {
                foreach (WormholeObject wormholeObj in wormholeObjs)
                {
                    wormholeObj.m_Obj.withinWormholeRadius = false;
                    foreach (Wormhole wormhole in wormholes)
                    {
                        Vector3 vecBetween = wormholeObj.m_Obj.m_vWorldPosition - wormhole.m_vWorldPosition;
                        float dist = vecBetween.Length();
                        if (wormholeObj.m_fMinDist <= 0 || dist < wormholeObj.m_fMinDist)
                        {
                            wormholeObj.m_Obj.withinWormholeRadius = true;
                            break;
                        }
                    }
                }
                base.Update(gameTime);
            }
        }

        public void CalculateIdealNumWormholes()
        {
            ShrinkingStarSphere shrinkingStarSphere = ShrinkingStarSphere.GetShrinkingStarSphere();
            float volume = MathHelper.Pi * (float)Math.Pow(shrinkingStarSphere.GetRadius(), 2.0);
            m_iIdealNumWormholePairs = (int)(volume * WormholeManager.WormholePairDensity);
        }

        /// <summary>
        /// Mark actor to be affected by all blackholes.
        /// </summary>
        /// <param name="actor"></param>
        public void AddToScope(Actor actor, float multiplier = 1.0f, float minDist = 0f)
        {
            wormholeObjs.Add(new WormholeObject(actor, multiplier, minDist));
        }

        /// <summary>
        /// Remove the actor from being affected by blackholes.
        /// </summary>
        /// <param name="actor"></param>
        public void RemoveFromScope(Actor actor)
        {
            foreach (WormholeObject obj in wormholeObjs)
            {
                if (obj.m_Obj == actor)
                    wormholeObjs.Remove(obj);
            }
        }

        /// <summary>
        /// Create a blackhole pair (in one out the other)
        /// </summary>
        public Tuple<Wormhole, Wormhole> CreateWormholePair(int lifetime, float magnitudeRatio1, float magnitudeRatio2, float speed1, float speed2)
        {
            // Create wormholes.
            Wormhole one = new Wormhole(Game, lifetime, magnitudeRatio1, speed1);
            Wormhole two = new Wormhole(Game, lifetime, magnitudeRatio2, speed2);

            // Set partnership
            one.SetPartner(two);
            two.SetPartner(one);

            // Add to our list to manage.
            wormholes.Add(one);
            wormholes.Add(two);

            // Add to the game.
            Game.Components.Add(one);
            Game.Components.Add(two);

            // Determine how many blackholes should be in this size sphere.
            CalculateIdealNumWormholes();

            // Create more wormholes if necessary or let some die out.
            while (wormholes.Count / 2 < m_iIdealNumWormholePairs)
                CreateRandomWormholePair();

            return new Tuple<Wormhole, Wormhole>(one, two);
        }

        /// <summary>
        /// Create a blackhole pair (in one out the other)
        /// </summary>
        public void CreateRandomWormholePair()
        {
            int lifetime = rand.Next(WormholeManager.MinWormholePairLifetime, WormholeManager.MaxWormholePairLifetime);

            float magnitudeRatio1 = WormholeManager.MinWormholeMagnitudeRatio + (float)rand.NextDouble() * (WormholeManager.MaxWormholeMagnitudeRatio - WormholeManager.MinWormholeMagnitudeRatio);
            float speed1 = WormholeManager.MinWormholeSpeed + (float)rand.NextDouble() * (WormholeManager.MaxWormholeSpeed - WormholeManager.MinWormholeSpeed);
            
            float magnitudeRatio2 = WormholeManager.MinWormholeMagnitudeRatio + (float)rand.NextDouble() * (WormholeManager.MaxWormholeMagnitudeRatio - WormholeManager.MinWormholeMagnitudeRatio);
            float speed2 = WormholeManager.MinWormholeSpeed + (float)rand.NextDouble() * (WormholeManager.MaxWormholeSpeed - WormholeManager.MinWormholeSpeed);

            CreateWormholePair(lifetime, magnitudeRatio1, magnitudeRatio2, speed1, speed2);
        }

        /// <summary>
        /// Delete a pair of wormholes.
        /// 
        /// </summary>
        /// <param name="wormhole"></param>
        public void RemoveWormhole(Wormhole wormhole)
        {
            // Remove the reference to this wormhole in its partner.
            if (wormhole.GetPartner() != null)
                wormhole.GetPartner().SetPartner(null);

            wormholes.Remove(wormhole);
            Game.Components.Remove(wormhole);

            // Determine how many blackholes should be in this size sphere.
            CalculateIdealNumWormholes();
            
            // Create more wormholes if necessary.
            if (wormholes.Count/2 < m_iIdealNumWormholePairs)
                CreateRandomWormholePair();
        }

        public void SetShip(Ship ship)
        {
            m_Ship = ship;
        }
    }
}
